# -*- coding: utf-8 -*-
"""
虚拟网盘API控制器
@api-version: 1.0.0
// REF: API-NETDISK-010
"""

import json
from datetime import datetime
from flask import Blueprint, request, jsonify, current_app
from werkzeug.utils import secure_filename

from backend.models import db
from backend.services.virtual_netdisk_service import VirtualNetdiskService
from backend.models.file_share import FileShare
from backend.models.virtual_file import VirtualFile
from backend.models.user_storage_quota import UserStorageQuota
from backend.utils.api_response import ApiResponse
from backend.utils.auth import login_required, get_current_user
from backend.utils.validators import validate_json, validate_file_upload

# 创建蓝图
netdisk_bp = Blueprint('netdisk', __name__, url_prefix='/netdisk')

# 初始化服务
netdisk_service = VirtualNetdiskService()

@netdisk_bp.route('/upload/check', methods=['POST'])
@login_required
def check_file_md5():
    """检查文件MD5是否可以秒传
    // REF: API-NETDISK-001-A
    """
    try:
        user = get_current_user()
        
        # 额外的用户验证（防御性编程）
        if not user:
            return ApiResponse.error("未授权访问", code=401).to_response() or {}
        data = request.get_json() or {}
        
        file_md5 = data.get('md5')
        filename = data.get('filename')
        file_size = data.get('fileSize', 0)
        parent_id = data.get('parentId')
        
        if not file_md5 or not filename:
            return ApiResponse.error("MD5和文件名不能为空", code=400).to_response()
        
        # 调用服务层检查秒传
        result = netdisk_service.check_instant_upload(
            user_id=user.id,
            file_md5=file_md5,
            filename=filename,
            file_size=file_size,
            parent_id=parent_id
        )
        
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"检查秒传失败: {str(e)}", code=500).to_response()


@netdisk_bp.route('/upload', methods=['POST'])
@login_required
def upload_file():
    """文件上传（实际文件上传）
    // REF: API-NETDISK-001-B
    """
    try:
        user = get_current_user()
        
        # 检查用户是否有实际文件上传权限
        if not user.can_upload_files:
            return ApiResponse.error("您没有文件上传权限，仅支持MD5秒传", code=403).to_response()
        
        # 验证文件上传
        if 'file' not in request.files:
            return ApiResponse.error("未选择文件", code=400).to_response()
        
        file = request.files['file']
        if file.filename == '':
            return ApiResponse.error("文件名不能为空", code=400).to_response()
        
        # 获取参数
        parent_id = request.form.get('parentId', type=int)
        file_md5 = request.form.get('md5')
        chunk_index = request.form.get('chunkIndex', type=int)
        total_chunks = request.form.get('totalChunks', type=int)
        
        # 文件名安全处理
        filename = secure_filename(file.filename)
        if not filename:
            return ApiResponse.error("文件名无效", code=400).to_response()
        
        file.filename = filename
        
        # 分片上传处理（如果需要）
        if chunk_index is not None and total_chunks is not None:
            # TODO: 实现分片上传逻辑
            return ApiResponse.error("分片上传暂未实现", code=501).to_response()
        
        # 调用服务层上传
        result = netdisk_service.upload_file(
            user_id=user.id,
            file=file,
            parent_id=parent_id,
            file_md5=file_md5,
            force_upload=True  # 强制上传实际文件
        )
        
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"上传失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/download/<int:file_id>', methods=['GET'])
@login_required
def download_file(file_id):
    """文件下载
    // REF: API-NETDISK-002
    """
    try:
        user = get_current_user()
        
        # 获取下载链接
        result = netdisk_service.get_download_url(user.id, file_id)
        
        if result.success:
            # 重定向到云存储下载链接
            from flask import redirect
            return redirect(result.data['downloadUrl'])
        else:
            return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"下载失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/download/<path:file_path>', methods=['GET'])
@login_required
def download_file_by_path(file_path):
    """按文件路径下载文件（用于本地存储的MD5分目录文件）
    // REF: API-NETDISK-002-B-OPTIMIZED
    """
    try:
        from flask import send_file, current_app, request, Response
        import os
        import mimetypes
        from datetime import datetime
        from models.file_operation_log import FileOperationLog, OperationType
        
        user = get_current_user()
        
        # 额外的用户验证（防御性编程）
        if not user:
            return ApiResponse.error("未授权访问", code=401).to_response()
        
        # 增强安全检查
        if '..' in file_path or file_path.startswith('/') or '\\' in file_path:
            return ApiResponse.error("非法的文件路径", code=403).to_response()
        
        # 文件扩展名检查
        allowed_extensions = current_app.config.get('ALLOWED_EXTENSIONS', set())
        if allowed_extensions and not any(file_path.lower().endswith(f'.{ext}') for ext in allowed_extensions):
            return ApiResponse.error("不支持的文件类型", code=403).to_response()
        
        # 构建完整的文件路径
        storage_path = current_app.config.get('CLOUD_STORAGE_CONFIG', {}).get('local', {}).get('storage_path', 'uploads')
        full_path = os.path.join(storage_path, file_path)
        
        # 安全检查：确保文件路径在允许的存储目录内
        if not os.path.abspath(full_path).startswith(os.path.abspath(storage_path)):
            return ApiResponse.error("非法的文件路径", code=403).to_response()
        
        # 检查文件是否存在
        if not os.path.exists(full_path):
            return ApiResponse.error("文件不存在", code=404).to_response()
        
        # 获取文件信息
        file_size = os.path.getsize(full_path)
        file_mtime = os.path.getmtime(full_path)
        
        # 检测文件类型
        content_type, _ = mimetypes.guess_type(full_path)
        if not content_type:
            content_type = 'application/octet-stream'
        
        # 处理Range请求（断点续传）
        range_header = request.headers.get('Range')
        if range_header:
            try:
                # 解析Range头: bytes=start-end
                range_match = range_header.replace('bytes=', '').split('-')
                byte_start = int(range_match[0]) if range_match[0] else 0
                byte_end = int(range_match[1]) if range_match[1] else file_size - 1
                
                # 验证范围
                if byte_start >= file_size or byte_end >= file_size or byte_start > byte_end:
                    return Response(
                        'Requested Range Not Satisfiable',
                        status=416,
                        headers={'Content-Range': f'bytes */{file_size}'}
                    )
                
                # 读取文件片段
                with open(full_path, 'rb') as f:
                    f.seek(byte_start)
                    data = f.read(byte_end - byte_start + 1)
                
                # 创建部分内容响应
                response = Response(
                    data,
                    status=206,
                    headers={
                        'Content-Type': content_type,
                        'Content-Range': f'bytes {byte_start}-{byte_end}/{file_size}',
                        'Accept-Ranges': 'bytes',
                        'Content-Length': str(len(data)),
                        'Cache-Control': 'private, max-age=3600',
                        'Last-Modified': datetime.fromtimestamp(file_mtime).strftime('%a, %d %b %Y %H:%M:%S GMT')
                    }
                )
                
                # 记录部分下载日志
                FileOperationLog.log_operation(
                    user_id=user.id,
                    operation_type=OperationType.DOWNLOAD,
                    target_path=file_path,
                    file_size=len(data),
                    ip_address=request.remote_addr,
                    user_agent=request.headers.get('User-Agent', '')[:500],
                    operation_detail=f"Range: {byte_start}-{byte_end}/{file_size}"
                )
                
                return response
                
            except (ValueError, IndexError):
                # Range头格式错误，忽略并返回完整文件
                pass
        
        # 创建完整文件响应
        response = send_file(
            full_path,
            as_attachment=True,
            download_name=os.path.basename(file_path),
            mimetype=content_type
        )
        
        # 设置缓存和其他响应头
        response.headers['Cache-Control'] = 'private, max-age=3600'
        response.headers['Accept-Ranges'] = 'bytes'
        response.headers['Last-Modified'] = datetime.fromtimestamp(file_mtime).strftime('%a, %d %b %Y %H:%M:%S GMT')
        
        # 记录下载操作日志
        try:
            FileOperationLog.log_operation(
                user_id=user.id,
                operation_type=OperationType.DOWNLOAD,
                target_path=file_path,
                file_size=file_size,
                ip_address=request.remote_addr,
                user_agent=request.headers.get('User-Agent', '')[:500],
                operation_detail=f"完整文件下载"
            )
        except Exception as log_error:
            # 日志记录失败不应影响文件下载
            current_app.logger.warning(f"下载日志记录失败: {str(log_error)}")
        
        return response
        
    except Exception as e:
        current_app.logger.error(f"文件下载失败: {str(e)}, 文件路径: {file_path}")
        return ApiResponse.error(f"下载失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/download-url/<int:file_id>', methods=['GET'])
@login_required
def get_download_url(file_id):
    """获取下载链接
    // REF: API-NETDISK-002
    """
    try:
        user = get_current_user()
        result = netdisk_service.get_download_url(user.id, file_id)
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"获取下载链接失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files', methods=['GET'])
@login_required
def list_files():
    """文件列表
    // REF: API-NETDISK-003
    """
    print("🚨 list_files函数被调用了！")
    try:
        user = get_current_user()
        print(f"🚨 用户: {user.email if user else 'None'}")
        
        # 获取参数
        parent_id = request.args.get('parentId', type=int)
        page = request.args.get('page', 1, type=int)
        page_size = request.args.get('pageSize', 20, type=int)
        folders_only = request.args.get('foldersOnly', 'false').lower() == 'true'
        
        # 调试日志
        print(f"🔍 API参数调试: parentId={parent_id}, foldersOnly={request.args.get('foldersOnly')}, folders_only={folders_only}")
        
        # 限制分页大小
        page_size = min(page_size, 100)
        
        result = netdisk_service.list_files(
            user_id=user.id,
            parent_id=parent_id,
            page=page,
            page_size=page_size,
            folders_only=folders_only
        )
        
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"获取文件列表失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/delete', methods=['DELETE'])
@login_required
@validate_json(['ids'])
def delete_files(data):
    """删除文件
    // REF: API-NETDISK-004
    """
    try:
        user = get_current_user()
        
        file_ids = data.get('ids', [])
        if not isinstance(file_ids, list) or not file_ids:
            return ApiResponse.error("文件ID列表不能为空", code=400).to_response()
        
        result = netdisk_service.delete_files(user.id, file_ids)
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"删除失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/folders', methods=['POST'])
@login_required
@validate_json(['name'])
def create_folder(data):
    """创建文件夹
    // REF: API-NETDISK-005
    """
    try:
        user = get_current_user()
        
        name = data.get('name', '').strip()
        parent_id = data.get('parent_id')
        
        if not name:
            return ApiResponse.error("文件夹名称不能为空", code=400).to_response()
        
        result = netdisk_service.create_folder(
            user_id=user.id,
            name=name,
            parent_id=parent_id
        )
        
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"创建文件夹失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/<int:file_id>/move', methods=['PUT'])
@login_required
@validate_json(['targetParentId'])
def move_file(data, file_id):
    """移动/重命名文件
    // REF: API-NETDISK-006
    """
    try:
        user = get_current_user()
        
        target_parent_id = data.get('targetParentId')
        new_name = data.get('newName', '').strip()
        
        # 验证文件权限
        virtual_file = VirtualFile.query.filter_by(
            id=file_id, owner_id=user.id
        ).first()
        
        if not virtual_file:
            return ApiResponse.error("文件不存在", code=404).to_response()
        
        # 执行移动/重命名
        old_path = virtual_file.get_full_path()
        
        if target_parent_id is not None:
            virtual_file.move_to(target_parent_id)
        
        if new_name:
            virtual_file.name = new_name
            virtual_file.updated_at = datetime.utcnow()
        
        db.session.commit()
        
        # 记录操作日志
        from models.file_operation_log import FileOperationLog, OperationType
        FileOperationLog.log_operation(
            user_id=user.id,
            operation_type=OperationType.MOVE if target_parent_id else OperationType.RENAME,
            file_id=file_id,
            source_path=old_path,
            target_path=virtual_file.get_full_path()
        )
        
        return ApiResponse.success({
            'id': virtual_file.id,
            'newPath': virtual_file.get_full_path(),
            'message': '操作成功'
        }).to_response()
        
    except Exception as e:
        from models import db
        db.session.rollback()
        return ApiResponse.error(f"操作失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/<int:file_id>/copy', methods=['POST'])
@login_required
@validate_json(['targetParentId'])
def copy_file(data, file_id):
    """复制文件
    // REF: API-NETDISK-007
    """
    try:
        user = get_current_user()
        
        target_parent_id = data.get('targetParentId')
        
        # 验证文件权限
        virtual_file = VirtualFile.query.filter_by(
            id=file_id, owner_id=user.id
        ).first()
        
        if not virtual_file:
            return ApiResponse.error("文件不存在", code=404).to_response()
        
        # 执行复制
        new_file = virtual_file.copy_to(target_parent_id)
        
        # 如果是文件，增加存储引用计数
        if not virtual_file.is_folder and virtual_file.storage_file_id:
            from models.virtual_storage import VirtualStorage
            storage = VirtualStorage.query.filter_by(
                file_id=virtual_file.storage_file_id
            ).first()
            if storage:
                storage.increment_reference()
        
        # 更新用户配额
        quota = UserStorageQuota.get_or_create(user.id)
        if virtual_file.is_folder:
            quota.add_folder()
        else:
            quota.add_file(virtual_file.file_size)
        
        db.session.commit()
        
        # 记录操作日志
        from models.file_operation_log import FileOperationLog, OperationType
        FileOperationLog.log_copy(
            user_id=user.id,
            file_id=new_file.id,
            source_path=virtual_file.get_full_path(),
            target_path=new_file.get_full_path()
        )
        
        return ApiResponse.success({
            'newId': new_file.id,
            'newPath': new_file.get_full_path(),
            'referenceCount': storage.reference_count if storage else 0,
            'message': '复制成功'
        }).to_response()
        
    except Exception as e:
        from models import db
        db.session.rollback()
        return ApiResponse.error(f"复制失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/<int:file_id>/info', methods=['GET'])
@login_required
def get_file_info(file_id):
    """获取文件信息
    // REF: API-NETDISK-008
    """
    try:
        user = get_current_user()
        
        # 验证文件权限
        virtual_file = VirtualFile.query.filter_by(
            id=file_id, owner_id=user.id
        ).first()
        
        if not virtual_file:
            return ApiResponse.error("文件不存在", code=404).to_response()
        
        file_data = virtual_file.to_dict()
        
        # 添加存储信息
        if not virtual_file.is_folder and virtual_file.storage_file_id:
            from models.virtual_storage import VirtualStorage
            storage = VirtualStorage.query.filter_by(
                file_id=virtual_file.storage_file_id
            ).first()
            
            if storage:
                file_data.update({
                    'storageInfo': storage.to_dict(),
                    'signature': virtual_file.storage_file_id,
                    'referenceCount': storage.reference_count
                })
        
        return ApiResponse.success(file_data).to_response()
        
    except Exception as e:
        return ApiResponse.error(f"获取文件信息失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/<int:file_id>/share', methods=['POST'])
@login_required
def create_share(file_id):
    """创建文件分享
    // REF: API-NETDISK-009
    """
    try:
        user = get_current_user()
        data = request.get_json() or {}
        
        expire_hours = data.get('expireHours', 24)
        password = data.get('password', '')
        
        # 验证文件权限
        virtual_file = VirtualFile.query.filter_by(
            id=file_id, owner_id=user.id
        ).first()
        
        if not virtual_file:
            return ApiResponse.error("文件不存在", code=404).to_response()
        
        # 创建分享
        share = FileShare.create_share(
            file_id=file_id,
            sharer_id=user.id,
            password=password,
            expire_hours=expire_hours
        )
        
        # 记录操作日志
        from models.file_operation_log import FileOperationLog
        FileOperationLog.log_share(
            user_id=user.id,
            file_id=file_id,
            target_path=virtual_file.get_full_path(),
            operation_detail=json.dumps({
                'share_id': share.share_id,
                'expire_hours': expire_hours,
                'has_password': bool(password)
            })
        )
        
        return ApiResponse.success({
            'shareId': share.share_id,
            'shareUrl': share.get_share_url(),
            'expiresAt': share.expires_at.isoformat() if share.expires_at else None,
            'message': '分享创建成功'
        }).to_response()
        
    except Exception as e:
        return ApiResponse.error(f"创建分享失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/shares/<share_id>', methods=['GET'])
def get_share_info(share_id):
    """获取分享信息
    // REF: API-NETDISK-010
    """
    try:
        share = FileShare.get_by_share_id(share_id)
        
        if not share or not share.is_valid():
            return ApiResponse.error("分享不存在或已过期", code=404).to_response()
        
        # 增加访问次数
        share.increment_access_count()
        
        # 获取文件信息
        virtual_file = share.file
        if not virtual_file:
            return ApiResponse.error("分享的文件不存在", code=404).to_response()
        
        share_data = share.to_dict()
        share_data.update({
            'fileInfo': {
                'id': virtual_file.id,
                'name': virtual_file.name,
                'isFolder': virtual_file.is_folder,
                'fileSize': virtual_file.file_size,
                'createdAt': virtual_file.created_at.isoformat()
            },
            'sharerInfo': {
                'nickname': share.sharer.nickname if share.sharer else '未知用户'
            }
        })
        
        return ApiResponse.success(share_data).to_response()
        
    except Exception as e:
        return ApiResponse.error(f"获取分享信息失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/storage/info', methods=['GET'])
@login_required
def get_storage_info():
    """获取存储信息
    // REF: API-NETDISK-011
    """
    try:
        user = get_current_user()
        result = netdisk_service.get_user_storage_info(user.id)
        return result.to_response()
        
    except Exception as e:
        return ApiResponse.error(f"获取存储信息失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/search', methods=['GET'])
@login_required
def search_files():
    """搜索文件
    // REF: API-NETDISK-012
    """
    try:
        user = get_current_user()
        
        query = request.args.get('q', '').strip()
        parent_id = request.args.get('parentId', type=int)
        
        if not query:
            return ApiResponse.error("搜索关键词不能为空", code=400).to_response()
        
        # 构建搜索查询
        search_query = VirtualFile.query.filter(
            VirtualFile.owner_id == user.id,
            VirtualFile.name.contains(query)
        )
        
        # 限制搜索范围
        if parent_id:
            search_query = search_query.filter(VirtualFile.parent_id == parent_id)
        
        files = search_query.limit(50).all()
        
        # 构建结果
        results = []
        for file in files:
            file_data = file.to_dict()
            file_data['path'] = file.get_full_path()
            results.append(file_data)
        
        return ApiResponse.success({
            'results': results,
            'total': len(results),
            'query': query
        }).to_response()
        
    except Exception as e:
        return ApiResponse.error(f"搜索失败: {str(e)}", code=500).to_response()

@netdisk_bp.route('/files/by-category', methods=['GET'])
@login_required
def get_files_by_category():
    """按文件类别查询
    // REF: API-NETDISK-013
    """
    try:
        user = get_current_user()
        
        category = request.args.get('category', '').lower()
        page = request.args.get('page', 1, type=int)
        page_size = request.args.get('pageSize', 20, type=int)
        
        # 限制分页大小
        page_size = min(page_size, 100)
        
        # 媒体类型映射
        category_mapping = {
            'document': [1],  # 文档
            'image': [2],     # 图片
            'video': [3],     # 视频
            'audio': [4],     # 音频
            'other': [5]      # 其他
        }
        
        if category not in category_mapping:
            return ApiResponse.error("不支持的文件类别", code=400).to_response()
        
        media_types = category_mapping[category]
        
        # 查询文件
        from models.virtual_storage import VirtualStorage
        query = VirtualFile.query.join(VirtualStorage, 
            VirtualFile.storage_file_id == VirtualStorage.file_id
        ).filter(
            VirtualFile.owner_id == user.id,
            VirtualFile.is_folder == False,
            VirtualStorage.media_type.in_(media_types)
        ).order_by(VirtualFile.updated_at.desc())
        
        pagination = query.paginate(
            page=page, per_page=page_size, error_out=False
        )
        
        # 构建结果
        items = []
        for file in pagination.items:
            file_data = file.to_dict()
            file_data['path'] = file.get_full_path()
            items.append(file_data)
        
        return ApiResponse.success({
            'items': items,
            'total': pagination.total,
            'page': page,
            'pageSize': page_size,
            'totalPages': pagination.pages,
            'category': category
        }).to_response()
        
    except Exception as e:
        return ApiResponse.error(f"查询失败: {str(e)}", code=500).to_response()

# 错误处理
@netdisk_bp.errorhandler(413)
def file_too_large(error):
    """文件过大错误处理"""
    return ApiResponse.error("文件过大", code=413).to_response()

@netdisk_bp.errorhandler(400)
def bad_request(error):
    """请求错误处理"""
    return ApiResponse.error("请求参数错误", code=400).to_response()